/**
 * @Author       : Mo 2427995270@qq.com
 * @Date         : 2022-01-24 13:13:33
 * @LastEditTime : 2025-09-03 14:32:35
 * @FilePath     : \GD32F407-TEMP\hal_driver\hal_i2c_master.c
 * @Description  : 软件i2c源文件
 * @Version      : V1.0
 * @History      : V1.0
 * @Note         : 调用过I2C函数后产生的错误标志会被下次的调用覆盖
 */
/***************************************Includes***********************************/
#include "hal_i2c_master.h"


/************************************Private Macros********************************/



/**********************************Private Statement*******************************/
static void i2c_lock(i2c_master_bus_t *bus_handle);
static void i2c_unlock(i2c_master_bus_t *bus_handle);
/**
 * @brief I2C起始信号
 * @param  dev_handle
 * @note null
 */
static void i2c_start(i2c_master_dev_t *dev_handle);
/**
 * @brief I2C停止信号
 * @param  dev_handle
 * @note null
 */
static void i2c_stop(i2c_master_dev_t *dev_handle);
/**
 * @brief I2C等待应答
 * @param  dev_handle
 * @return int -1没有应答 0有应答
 * @note null
 */
static int i2c_wait_ack(i2c_master_dev_t *dev_handle);
/**
 * @brief I2C应答
 * @param  dev_handle
 * @note null
 */
static inline void i2c_ack(i2c_master_dev_t *dev_handle);
/**
 * @brief I2C不应答
 * @param  dev_handle
 * @note null
 */
static inline void i2c_nack(i2c_master_dev_t *dev_handle);
/**
 * @brief I2C发送一个字节
 * @param  dev_handle
 * @param  txd              发送的字节
 * @note null
 */
static void i2c_send_byte(i2c_master_dev_t *dev_handle, uint8_t txd);
/**
 * @brief I2C读取一个字节
 * @param  dev_handle
 * @param  ack              1应答 0非应答
 * @return uint32_t 读取的字节
 * @note null
 */
static uint32_t i2c_read_byte(i2c_master_dev_t *dev_handle, unsigned char ack);

/***********************************Public Variables*******************************/



/**********************************Private Variables*******************************/




/**********************************Public Functions********************************/
/**
 * @brief I2C总线初始化
 * @param  bus_handle       总线结构体指针
 * @param  delay_us         延时us
 * @param  delay_ms         延时ms
 * @param  port_scl         scl端口
 * @param  port_sda         sda端口
 * @param  pinnum_scl       scl管脚
 * @param  pinnum_sda       sda管脚
 * @param  pullup           1上拉 0无上拉
 * @return int 0成功 -1失败
 * @note null
 */
int i2c_master_bus_init(
    i2c_master_bus_t *bus_handle,
    void (*delay_us)(uint32_t us),
    void (*delay_ms)(uint32_t ms),
    uint32_t port_scl, uint32_t port_sda, 
    uint32_t pinnum_scl, uint32_t pinnum_sda,
    int pullup)
{
    bus_handle->delay_us = delay_us;
    bus_handle->delay_ms = delay_ms;
    int pupd = pullup ? GPIO_LL_PULL_UP : GPIO_LL_PULL_NO;
    gpio_ll_init(&bus_handle->gpio_scl, port_scl, pinnum_scl, GPIO_LL_MODE_OUTPUT, GPIO_LL_OTYPE_OD, pupd, GPIO_LL_SPEED_HIGH, 0);
    gpio_ll_init(&bus_handle->gpio_sda, port_sda, pinnum_sda, GPIO_LL_MODE_OUTPUT, GPIO_LL_OTYPE_OD, pupd, GPIO_LL_SPEED_HIGH, 0);
#if USE_OS == OS_FREERTOS
    bus_handle->mutex = xSemaphoreCreateMutex();
    i2c_unlock(bus_handle);
#endif
    return 0;
}
/**
 * @brief I2C总线添加设备
 * @param  bus_handle       总线结构体指针
 * @param  dev_handle       设备结构体指针
 * @param  dev_addr         设备地址 7位不包含读写位
 * @param  time_series_us   时序最低要求时间
 * @param  time_out_ms      应答超时时间
 * @return int 0成功 -1失败
 * @note null
 */
int i2c_master_bus_add_dev( i2c_master_bus_t *bus_handle, i2c_master_dev_t *dev_handle, uint8_t dev_addr, uint32_t time_series_us, uint32_t time_out_ms) {
    dev_handle->bus_handle = bus_handle;
    dev_handle->dev_addr = dev_addr;
    dev_handle->time_series_us = time_series_us;
    dev_handle->time_out_ms = time_out_ms;
    return 0;
}
/**
 * @brief I2C读取多个字节
 * @param  dev_handle
 * @param  buf              读取的数据存在这
 * @param  buf_len          读取数据的数量 byte
 * @return int 0成功 -1失败
 * @note null
 */
int i2c_master_read(i2c_master_dev_t *dev_handle, void *buf, int32_t buf_len) {
    i2c_lock(dev_handle->bus_handle);
    uint8_t *p = buf;
    i2c_start(dev_handle);
    i2c_send_byte(dev_handle, (dev_handle->dev_addr << 1) | 1);
    if (i2c_wait_ack(dev_handle) < 0) {
        i2c_stop(dev_handle);
        i2c_unlock(dev_handle->bus_handle);
        return -1;
    }
    for (int i = 0; i < buf_len - 1; i++) {
        p[i] = i2c_read_byte(dev_handle, 1);
    }
    p[buf_len - 1] = i2c_read_byte(dev_handle, 0);
    i2c_stop(dev_handle);
    i2c_unlock(dev_handle->bus_handle);
    return 0;
}
/**
 * @brief I2C写入多个字节
 * @param  dev_handle
 * @param  buf              要写入的数据
 * @param  buf_len          写入数据的数量 byte
 * @return int 0成功 -1失败
 * @note null
 */
int i2c_master_write(i2c_master_dev_t *dev_handle, const void *buf, int32_t buf_len) {
    i2c_lock(dev_handle->bus_handle);
    const uint8_t *p = buf;
    i2c_start(dev_handle);
    i2c_send_byte(dev_handle, dev_handle->dev_addr << 1);
    if (i2c_wait_ack(dev_handle) < 0) {
        i2c_stop(dev_handle);
        i2c_unlock(dev_handle->bus_handle);
        return -1;
    }
    for (int i = 0; i < buf_len; i++) {
        i2c_send_byte(dev_handle, p[i]);
        if (i2c_wait_ack(dev_handle) < 0) {
            dev_handle->i2c_error_code = I2C_ERROR_WRITE_NACK;
            i2c_stop(dev_handle);
            i2c_unlock(dev_handle->bus_handle);
            return -1;
        }
    }
    i2c_stop(dev_handle);
    i2c_unlock(dev_handle->bus_handle);
    return 0;
}
/**
 * @brief I2C先写后读
 * @param  dev_handle
 * @param  write_buf        要写入的数据
 * @param  write_buf_len    数据量
 * @param  read_buf         要读取的数据
 * @param  read_buf_len     数据量
 * @return int 0成功 -1失败
 * @note null
 */
int i2c_master_write_and_read(i2c_master_dev_t *dev_handle, const void *write_buf, int32_t write_buf_len, void *read_buf, int32_t read_buf_len) {
    i2c_lock(dev_handle->bus_handle);
    const uint8_t *p = write_buf;
    i2c_start(dev_handle);
    i2c_send_byte(dev_handle, dev_handle->dev_addr << 1);
    if (i2c_wait_ack(dev_handle) < 0) {
        i2c_stop(dev_handle);
        i2c_unlock(dev_handle->bus_handle);
        return -1;
    }
    for (int i = 0; i < write_buf_len; i++) {
        i2c_send_byte(dev_handle, p[i]);
        if (i2c_wait_ack(dev_handle) < 0) {
            dev_handle->i2c_error_code = I2C_ERROR_WRITE_NACK;
            i2c_stop(dev_handle);
            i2c_unlock(dev_handle->bus_handle);
            return -1;
        }
    }
    i2c_start(dev_handle);
    i2c_send_byte(dev_handle, (dev_handle->dev_addr << 1) | 1);
    if (i2c_wait_ack(dev_handle) < 0) {
        i2c_stop(dev_handle);
        i2c_unlock(dev_handle->bus_handle);
        return -1;
    }
    for (int i = 0; i < read_buf_len - 1; i++) {
        ((uint8_t *)read_buf)[i] = i2c_read_byte(dev_handle, 1);
    }
    ((uint8_t *)read_buf)[read_buf_len - 1] = i2c_read_byte(dev_handle, 0);
    i2c_stop(dev_handle);
    i2c_unlock(dev_handle->bus_handle);
    return 0;
}
/**
 * @brief I2C连写(只有一次起始和停止信号)
 * @param  dev_handle
 * @param  buffer           存多组数据数组的指针，数组的每个元素是一个指向数据的指针
 * @param  buffer_len       存多组数据长度的数组的指针，数组的每个元素是数据的长度
 * @param  buffer_num       存多组数据的数组的长度
 * @return int 0成功 -1失败
 * @note null
 */
int i2c_master_multi_write(i2c_master_dev_t *dev_handle, const void *(*buffer)[], int32_t (*buffer_len)[], int32_t buffer_num) {
    i2c_lock(dev_handle->bus_handle);
    i2c_start(dev_handle);
    i2c_send_byte(dev_handle, dev_handle->dev_addr << 1);
    if (i2c_wait_ack(dev_handle) < 0) {
        i2c_stop(dev_handle);
        i2c_unlock(dev_handle->bus_handle);
        return -1;
    }

    for (int i = 0; i < buffer_num; i++) {
        const uint8_t *p = (*buffer)[i];
        for (int j = 0; j < (*buffer_len)[i]; j++) {
            i2c_send_byte(dev_handle, p[j]);
            if (i2c_wait_ack(dev_handle) < 0) {
                dev_handle->i2c_error_code = I2C_ERROR_WRITE_NACK;
                i2c_stop(dev_handle);
                i2c_unlock(dev_handle->bus_handle);
                return -1;
            }
        }
    }
    i2c_stop(dev_handle);
    i2c_unlock(dev_handle->bus_handle);
    return 0;
}
/**
 * @brief 等待SCL被器件释放
 * @param  dev_handle
 * @param  ms                 超时时间 ms
 * @return int -1超时没有释放 0释放
 * @note null
 */
int i2c_master_wait_scl_freed(i2c_master_dev_t *dev_handle, uint32_t ms) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;
    uint32_t time_out_us = ms * 1000 / 10; // 1 = 10us

    gpio_ll_set_output(&bus_handle->gpio_scl, 1);
    gpio_ll_set_mode(&bus_handle->gpio_scl, GPIO_LL_MODE_INPUT);
    delay_us(dev_handle->time_series_us);
    while (gpio_ll_get_input(&bus_handle->gpio_scl)) {
        if (!(--time_out_us)) {
            dev_handle->i2c_error_code = I2C_ERROR_NACK;
            return -1;
        }
        delay_us(10);
    }
    gpio_ll_set_mode(&bus_handle->gpio_scl, GPIO_LL_MODE_OUTPUT);
    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    delay_us(dev_handle->time_series_us);
    return 0;
}
/**********************************Private Functions*******************************/

static void i2c_lock(i2c_master_bus_t *bus_handle) {
#if USE_OS == OS_FREERTOS
    if (bus_handle->mutex != NULL) {
        xSemaphoreTake(bus_handle->mutex, portMAX_DELAY);
    }
#endif
}

static void i2c_unlock(i2c_master_bus_t *bus_handle) {
#if USE_OS == OS_FREERTOS
    if (bus_handle->mutex != NULL) {
        xSemaphoreGive(bus_handle->mutex);
    }
#endif
}

/**
 * @brief I2C起始信号
 * @param  dev_handle
 * @note null
 */
static void i2c_start(i2c_master_dev_t *dev_handle) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;

    gpio_ll_set_mode(&bus_handle->gpio_scl, GPIO_LL_MODE_OUTPUT);
    gpio_ll_set_mode(&bus_handle->gpio_sda, GPIO_LL_MODE_OUTPUT);
    gpio_ll_set_output(&bus_handle->gpio_sda, 1);
    gpio_ll_set_output(&bus_handle->gpio_scl, 1);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_sda, 0);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    delay_us(dev_handle->time_series_us);
}
/**
 * @brief I2C停止信号
 * @param  dev_handle
 * @note null
 */
static void i2c_stop(i2c_master_dev_t *dev_handle) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;

    gpio_ll_set_mode(&bus_handle->gpio_sda, GPIO_LL_MODE_OUTPUT);
    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    gpio_ll_set_output(&bus_handle->gpio_sda, 0);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_scl, 1);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_sda, 1);
    delay_us(dev_handle->time_series_us);
}
/**
 * @brief I2C等待应答
 * @param  dev_handle
 * @return int -1没有应答 0有应答
 * @note null
 */
static int i2c_wait_ack(i2c_master_dev_t *dev_handle) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;
    uint32_t time_out_us = dev_handle->time_out_ms * 1000 / 10; // 1 = 10us

    gpio_ll_set_output(&bus_handle->gpio_sda, 1);
    gpio_ll_set_mode(&bus_handle->gpio_sda, GPIO_LL_MODE_INPUT);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_scl, 1);
    delay_us(dev_handle->time_series_us);
    while (gpio_ll_get_input(&bus_handle->gpio_sda)) {
        if (!(--time_out_us)) {
            i2c_stop(dev_handle);
            dev_handle->i2c_error_code = I2C_ERROR_NACK;
            return -1;
        }
        delay_us(10);
    }
    dev_handle->i2c_error_code = I2C_ERROR_NONE;
    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_mode(&bus_handle->gpio_sda, GPIO_LL_MODE_OUTPUT);
    delay_us(dev_handle->time_series_us);
    return 0;
}
/**
 * @brief I2C应答
 * @param  dev_handle
 * @note null
 */
static inline void i2c_ack(i2c_master_dev_t *dev_handle) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;

    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_mode(&bus_handle->gpio_sda, GPIO_LL_MODE_OUTPUT);
    gpio_ll_set_output(&bus_handle->gpio_sda, 0);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_scl, 1);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    delay_us(dev_handle->time_series_us);
}
/**
 * @brief I2C不应答
 * @param  dev_handle
 * @note null
 */
static inline void i2c_nack(i2c_master_dev_t *dev_handle) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;

    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_mode(&bus_handle->gpio_sda, GPIO_LL_MODE_OUTPUT);
    gpio_ll_set_output(&bus_handle->gpio_sda, 1);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_scl, 1);
    delay_us(dev_handle->time_series_us);
    gpio_ll_set_output(&bus_handle->gpio_scl, 0);
    delay_us(dev_handle->time_series_us);
}
/**
 * @brief I2C发送一个字节
 * @param  dev_handle
 * @param  txd              发送的字节
 * @note null
 */
static void i2c_send_byte(i2c_master_dev_t *dev_handle, uint8_t txd) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;

    uint8_t t;
    for (t = 0; t < 8; t++) {
        if (txd & 0x80)
            gpio_ll_set_output(&bus_handle->gpio_sda, 1);
        else
            gpio_ll_set_output(&bus_handle->gpio_sda, 0);
        txd <<= 1;
        delay_us(dev_handle->time_series_us);
        gpio_ll_set_output(&bus_handle->gpio_scl, 1);
        delay_us(dev_handle->time_series_us);
        gpio_ll_set_output(&bus_handle->gpio_scl, 0);
        delay_us(dev_handle->time_series_us);
    }
}
/**
 * @brief I2C读取一个字节
 * @param  dev_handle
 * @param  ack              1应答 0非应答
 * @return uint32_t 读取的字节
 * @note null
 */
static uint32_t i2c_read_byte(i2c_master_dev_t *dev_handle, unsigned char ack) {
    i2c_master_bus_t *bus_handle = dev_handle->bus_handle;
    void (*delay_us)(uint32_t us) = bus_handle->delay_us;

    unsigned char i, receive = 0;
    gpio_ll_set_mode(&bus_handle->gpio_sda, GPIO_LL_MODE_INPUT);
    for (i = 0; i < 8; i++) {
        gpio_ll_set_output(&bus_handle->gpio_scl, 0);
        delay_us(dev_handle->time_series_us);
        gpio_ll_set_output(&bus_handle->gpio_scl, 1);
        delay_us(dev_handle->time_series_us);
        receive <<= 1;
        if (gpio_ll_get_input(&bus_handle->gpio_sda))
            receive++;
        delay_us(dev_handle->time_series_us);
    }
    if (ack)
        i2c_ack(dev_handle);
    else
        i2c_nack(dev_handle);
    return receive;
}

/* [] END OF FILE */
